﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using Mimoza.Transport;

namespace Mimoza.AgentLayer
{
    /// <summary>
    /// Class for synchronizy policy from server to agent
    /// </summary>
    public class PolicySynchronizer : BaseObject
    {
        public PolicySynchronizer(HostConnection hostConnection, Configuration config)
            : base(config)
        {
            m_hostConnection = hostConnection;
        }

        public override void Init() 
        {
            RegisterInTransport();
            
            m_hostConnection.ConnectionCreatedHandler += OnServerConnection;

            m_policyManager = new PolicyManager(Configuration);
            m_policyManager.Init();

            m_timeSynchronizer = new TimeSynchronizer(Configuration);
        }

        public override void Shutdown() 
        {
            m_hostConnection.ConnectionCreatedHandler -= OnServerConnection;
            m_policyManager.Shutdown();
        }

        void OnServerConnection(Connection conn)
        {
            SendHostInfoOnce(conn);
            SyncHost(conn);
        }

        void SyncHost(Connection conn)
        {
            Common.Logger.Log.Info("PolicySynchronizer (last syncId - '" + 
                m_policyManager.LastSyncId.ToString() + "')...");

            ObjectLayer.Host localHost = Configuration.AgentDatabase.GetLocalHost();
            if (localHost == null) return;

            Communication.RequestPolicySync reqPolicySync = new Communication.RequestPolicySync(
                m_policyManager.LastSyncId, localHost.ID, localHost.ModificationID);

            Communication.ReplyPolicySync replyPolicySync = conn.MakeRequest(reqPolicySync) 
                as Communication.ReplyPolicySync;

            StartAgentSync(conn, replyPolicySync);

            Common.Logger.Log.Info("PolicySynchronizer (syncId - '" + 
                m_policyManager.LastSyncId.ToString() + "')...OK");
    
        }

        void StartAgentSync(Connection conn, Communication.ReplyPolicySync replyPolicySync)
        {
            if (!replyPolicySync.IsSyncRequeried)
            {
                Common.Logger.Log.Info("PolicySynchronizer synchronization is not needed.");
                return;
            }

            Common.Logger.Log.Info("PolicySynchronizer serverSyncId - '" +
                  replyPolicySync.ServerSyncId.ToString() + "' HostModificationId - '" +
                  replyPolicySync.HostModifiedId.ToString() + "'. Start agent synchronization.");

            PolicySyncItem syncItem = LoadPoliciesFromServer(conn, replyPolicySync);

            m_policyManager.ApplySyncItem(syncItem);

            UpdateHostInfo(syncItem, conn);
        }

        PolicySyncItem LoadPoliciesFromServer(Connection conn, Communication.ReplyPolicySync replyPolicySync)
        {
            PolicySyncItem syncItem = new PolicySyncItem();
            syncItem.SyncId = replyPolicySync.ServerSyncId;

            Common.Logger.Log.Info("PolicySynchronizer LoadPoliciesFromServer...");

            ObjectLayer.Host localHost = Configuration.AgentDatabase.GetLocalHost();

            Communication.RequestPolicies reqPolicies = new Communication.RequestPolicies(
                    localHost.ID, m_policyManager.LastSyncId, localHost.ModificationID);

            Communication.ReplyPolicies replyPolicies = conn.MakeRequest(reqPolicies) as Communication.ReplyPolicies;

            if (replyPolicies.PoliciesRecords != null)
            {
                foreach (Mimoza.Common.PolicyRecord policyRec in replyPolicies.PoliciesRecords)
                {
                    Common.Logger.Log.Info("PolicySynchronizer Load Policy id - '" +
                        policyRec.ID.ToString() + "'.");

                    syncItem.AddPolicy(new Mimoza.ObjectLayer.Policy(Configuration.ObjectLayer,
                        policyRec));
                }
            }
            else
            {
                Common.Logger.Log.Info("PolicySynchronizer LoadPoliciesFromServer doesnt need to sync policy.");
            }

            if (replyPolicies.ServerHostRecord != null)
            {
                Common.Logger.Log.Info("PolicySynchronizer LoadPoliciesFromServer. Sync host setting.");

                ObjectLayer.Policy localHostPolicy = Configuration.AgentDatabase.GetLocalHostPluginPolicy();

                PutHostRecordToPolicy(localHostPolicy, replyPolicies.ServerHostRecord);
                localHostPolicy.DataModificationID = replyPolicies.ServerHostRecord.ModificationID;
                
                syncItem.AddPolicy(localHostPolicy);

                syncItem.HostModificationId = replyPolicies.ServerHostRecord.ModificationID;
            }

            return syncItem;
        }

        void UpdateHostInfo(PolicySyncItem syncItem, Connection conn)
        {
            bool bHasHostPolicy = false;
            foreach(ObjectLayer.Policy policy in syncItem.Policies)
            {
                if (policy.PluginID == Mimoza.PluginLayer.ExecutePluginManager.LocalHostPluginId)
                {
                    bHasHostPolicy = true;
                }
            }

            if (bHasHostPolicy)
            {
                Common.Logger.Log.Info("PolicySynchronizer.UpdateHostInfo send new information to server.");

                Configuration.HostRegistration.SendHostInfo(conn);
            }
        }

        void PutHostRecordToPolicy(ObjectLayer.Policy localHostPolicy, Common.HostRecord serverHostRecord)
        {
            XmlSerializer ser = new XmlSerializer(typeof(Common.HostRecord));
            System.IO.StringWriter stringStream = new System.IO.StringWriter();
            ser.Serialize(stringStream, serverHostRecord);
            localHostPolicy.Data = stringStream.ToString();
        }

        void SendHostInfoOnce(Connection conn)
        {
            if (!m_bNeedToSendHostInfo) return;
            m_bNeedToSendHostInfo = false;

            Configuration.HostRegistration.SendHostInfo(conn);
        }
    
        void RegisterInTransport()
        {
            Communication.RequestPolicySync.RegisterInTransport();
            Communication.ReplyPolicySync.RegisterInTransport();

            Communication.RequestPolicies.RegisterInTransport();
            Communication.ReplyPolicies.RegisterInTransport();
        }

        HostConnection m_hostConnection;
        PolicyManager m_policyManager;

        TimeSynchronizer m_timeSynchronizer;

        bool m_bNeedToSendHostInfo = true;
    }
}
